#ifndef CSG_TYPE_H
#define CSG_TYPE_H

#include "common.h"
#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

//!  A Vertex3 class.
/*!
  It is class of vertex in 3D coordinate, contains a Vector3 position and a Vector3 normal.
*/

class Vertex3
{
public:
    //! Position of the vertex.
    Vector3 pos;

    //! Normal of the vertex.
    Vector3 normal;

    //! A constructor.
    /*!
          \param _pos position value.
          \param _normal normal value.
          \return return a Vertex3.
    */
    Vertex3(Vector3 _pos, Vector3 _normal);

    //! Invert a vertex.
    void invert();

    //! Use Lerp to interpolate a vertex along a direction.
    /*!
          \param a the end vertex.
          \param t the parameter of the length from interpolation vertex to start vertex.
          \return return return the interpolation vertex.
    */
    Vertex3 interpolate(Vertex3 a, double t);
};

//!  A Axis-aligned bounding box class.
/*!
  It is Axis-aligned bounding box. It store the minimum position and the maximum position of the box.
*/
class AABBBox
{
public:
    //! The minimum position.
    Vector3 aabb_min;

    //! The maximum position.
    Vector3 aabb_max;

    //! A constructor.
    /*!
          It sets aabb_max to be -infinity and aabb_min to be infinity.
    */
    AABBBox();

    //! A constructor.
    /*!
          \param _aabb_min the minimum position.
          \param _aabb_max the maximum position.
    */
    AABBBox(Vector3 _aabb_min, Vector3 _aabb_max);

    //! Add a aabb to this aabb.
    /*!
          \param box an other aabb.
    */
    void addAABB(AABBBox box);

    //! Determine intersection with an other aabb.
    /*!
          \param box an other aabb.
    */
    bool AABBIntersectAABB(AABBBox box);
};

//! A polygon class.
/*!
  Represents a convex polygon.
*/
class MyPolygon
{
public:
    //! The normal of the polygon.
    Vector3 normal;

    //! The bounding box of this polygon.
    AABBBox box;

    //! The plane position of the polygon in plane function ax+by+cz+w=0.
    double w;

    //! All polygons that are clones of each other or were split from the same polygon will have the same share value.
    double share;

    //! All the vertices which represnet this polygon. The vertices in a polygon must be coplanar and form a convex loop.
    vector<Vertex3> vertices;

    //! A constructor.
    /*!
          \param _vertices all the vertices stored in a std::vector.
          \param _share the share property.
    */
    MyPolygon(vector<Vertex3> _vertices,double _share);

    //! Clone a new polygon from this polygon.
    MyPolygon* clone();

    //! Invert the polygon.
    void invert();

    //! Update polygon's AABB and plane after transform.
    void update();
};

//! A plane class.
/*!
  Represents a plane by function ax+by+cz+w=0, and (a, b, c) is the normal of the plane.
*/
class Plane
{
public:
    //! The normal of the plane.
    Vector3 normal;

    //! The position of the plane in function ax+by+cz+w=0.
    double w;

    //! A constructor.
    /*!
          \param _normal the normal of the plane.
          \param _w the position of the plane in function ax+by+cz+w=0.
    */
    Plane(Vector3 _normal,double _w);

    //! Clone a new plane from this plane.
    Plane* clone();

    //! Invert the plane.
    void invert();

    //! Use this plane to split a polygon.
    /*!
          \param polygon the splited polygon.
          \param coplanarFront if the plane and the polygon are coplanar and the polygon in the front of the plane, store the polygon in this std::vector.
          \param coplanarBack if the plane and the polygon are coplanar and the polygon in the back of the plane, store the polygon in this std::vector.
          \param front if the splited part of the polygon is in the in the front of the plane, store the splied part of polygon in this std::vector.
          \param back if the splited part of the polygon is in the in the back of the plane, store the splied part of polygon in this std::vector.
    */
    void splitPolygon(MyPolygon *polygon, vector<MyPolygon> &coplanarFront, vector<MyPolygon> &coplanarBack, vector<MyPolygon> &front, vector<MyPolygon> &back);
};

//! A tree class.
/*!
  Represents a mesh by a tree structure, we use kd-tree to accelerate the computarion.
*/
class Node
{
public:
    //! The split plane of this node.
    Plane* plane;

    //! The bounding box of this node.
    AABBBox box;

    //! The front child.
    Node* front;

    //! The back child.
    Node* back;

    //! Polygons in thie node.
    vector<MyPolygon> polygons;

    //! A constructor.
    /*!
          Construct a kd-tree by given polygons.
          \param polygons the polygons of the mesh.
    */
    Node(vector<MyPolygon> polygons);

    //! A constructor.
    /*!
          \param n a tree node.
    */
    Node(Node* n);

    //! A constructor.
    Node();

    //! Clone a new node from this node.
    Node* clone();

    //! Invert this tree.
    void invert();

    //! Split polygons by a plane.
    /*!
          \param polygons the polygons in the node.
    */
    vector<MyPolygon> clipPolygons(vector<MyPolygon> polygons);

    //! Remove parts of a tree inside another tree.
    /*!
          \param bsp another tree.
    */
    void clipTo(Node* bsp);

    //! Return all polygons in this tree.
    vector<MyPolygon> allPolygons();

    //! Add polygons to a tree.
    /*!
          \param polygons the polygons added to this tree.
    */
    void build(vector<MyPolygon> polygons);
};

#endif // CSG_TYPE_H
